package cn.tedu.charging.order.service.impl;

import cn.tedu.charging.common.constant.AmqpDeclareConst;
import cn.tedu.charging.common.constant.MqttTopicConst;
import cn.tedu.charging.common.pojo.message.DelayCheckMessage;
import cn.tedu.charging.common.pojo.message.StartCheckMessage;
import cn.tedu.charging.common.pojo.param.OrderAddParam;
import cn.tedu.charging.common.protocol.JsonResult;
import cn.tedu.charging.common.protocol.WebSocketResult;
import cn.tedu.charging.common.utils.CronUtil;
import cn.tedu.charging.common.utils.SnowflakeIdGenerator;
import cn.tedu.charging.common.utils.XxlJobTaskUtil;
import cn.tedu.charging.order.client.DeviceClient;
import cn.tedu.charging.order.client.UserClient;
import cn.tedu.charging.order.dao.repository.BillRepository;
import cn.tedu.charging.order.points.WebsocketServerPoint;
import cn.tedu.charging.order.pojo.po.ChargingBillExceptionPO;
import cn.tedu.charging.order.pojo.po.ChargingBillSuccessPO;
import cn.tedu.charging.order.producer.AmqpDelayProducer;
import cn.tedu.charging.order.producer.MqttProducer;
import cn.tedu.charging.order.service.OrderService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Date;

@Slf4j
@Service
public class OrderServiceImpl implements OrderService {
    @Autowired
    private DeviceClient deviceClient;

    @Autowired
    private UserClient userClient;

    @Autowired
    private SnowflakeIdGenerator idGenerator;

    @Autowired
    private MqttProducer mqttProducer;

    @Autowired
    private AmqpDelayProducer<DelayCheckMessage> amqpDelayProducer;

    @Autowired
    private BillRepository billRepository;

    @Autowired
    private WebsocketServerPoint  websocketServerPoint;

    @Override
    public String createOrder(OrderAddParam param) {
        //1.检查设备--轻做
        checkGunAvailabe(param.getGunId());
        //2.检查车主--轻做
        checkUserAvailabe(param.getUserId(), param.getGunId());
        //3.订单编号生成--雪花算法
        String billId=generateBillId();
        //4.和设备通信 发送开始充电命令--emqx异步消息
        sendStartCharging(billId,param);
        //5.发送延迟消息 对自检结果做补充操作--rabbitmq延迟逻辑
        sendDelayCheck(param,billId);
        //6.为兜底检查订单最终状态 发布长时间延迟定时任务--xxl-job客户端
        createBillTask(billId);
        //7.修改枪状态为 使用中
        return billId;
    }

    @Override
    public void orderStatusCheck(String billId) {
        //1.读取一个success
        ChargingBillSuccessPO success=billRepository.getSuccessByBillId(billId);
        //判断存在
        if (success!=null) {
            //判断status==1
            if (success.getBillStatus()!=null&&success.getBillStatus()==1){
                //2.创建异常单
                //2.1异常po对象组织一下
                ChargingBillExceptionPO exceptionPO=new ChargingBillExceptionPO();
                exceptionPO.setBillId(billId);
                exceptionPO.setBillStarttime(success.getChargingStartTime());
                exceptionPO.setCreateTime(new Date());
                exceptionPO.setDeleted(0);
                billRepository.saveExceptionBill(exceptionPO);
                //如果异常单某些属性没有,单独查询封装新增
                //3.修改success状态
                billRepository.updateSuccessStatus(billId,3);
                //TODO 4.通知用户充电订单异常结束
                //4.通知用户充电订单异常结束
                WebSocketResult<String> wsMessage=new WebSocketResult<>();
                wsMessage.setState(1);
                wsMessage.setMessage("订单异常结束");
                wsMessage.setData("您的订单没有正常充电结束,请拔枪结账走人");
                websocketServerPoint.pushMsg(success.getUserId(), String.valueOf(wsMessage));
            }else{
                log.debug("当前订单状态异常,长时间未充电结束");
            }
        }else{
            log.debug("成功订单不存在");
        }
    }

    private void createBillTask(String billId) {
        //1.先生成基于该订单的一个最大充电时间cron表达式
        //理论上来说 cron表达式 根据扫码下单时间 计算的最大充电时间应该有计算过程
        String expression = CronUtil.delayCron(1000 * 60 * 2);
        //2.调用工具直接访问另外一个系统 调度中心创建任务
        XxlJobTaskUtil.createJobTask(expression,"order-executor",billId);
    }

    private void sendDelayCheck(OrderAddParam param, String billId) {
        //1.封装接口DelayCheckMessage  缺少vehicleId
        DelayCheckMessage delayCheckMessage = new DelayCheckMessage();
        BeanUtils.copyProperties(param,delayCheckMessage);
        delayCheckMessage.setOrderNo(billId);
        //2.自定义AmqpDelayProducer发送延迟 1分钟(为什么1分钟,不是2分钟3分钟)
        amqpDelayProducer.sendDelay(
                AmqpDeclareConst.DELAY_BILL_EX,"",delayCheckMessage,1000*60);
    }

    private void sendStartCharging(String billId,OrderAddParam param) {
        StartCheckMessage message = new StartCheckMessage();
        message.setOrderNo(billId);
        message.setUserId(param.getUserId());
        message.setGunId(param.getGunId());
        //2.调用send封装 发送给设备 charging/device/check/pileId
        mqttProducer.send(
                MqttTopicConst.START_GUN_CHECK_PREFIX+param.getPileId(),message);
    }

    private String generateBillId() {
        return idGenerator.nextId()+"";
    }

    private void checkGunAvailabe(Integer gunId) {
        //调用发起
        JsonResult<Boolean> result = deviceClient.checkGun(gunId);
        Boolean available = result.getData();
        //判断返回值是否可用
        if (!available) {
            throw new RuntimeException("枪状态异常,不可充电");
        }
    }

    private void checkUserAvailabe(Integer userId, Integer gunId) {
        JsonResult<Boolean> result = userClient.chargeCheck(userId, gunId);
        Boolean available = result.getData();
        if (!available){
            throw new RuntimeException("车主或者车辆与当前充电设备不匹配");
        }
    }
}
